#ifndef SINGLETON_HPP
#define SINGLETON_HPP

#include <iostream>
#include <pthread.h>
#include <unistd.h>

// 饿汉实现的单例模式(线程安全版本)
class Singleton
{
private:
    Singleton()
    {
        pthread_mutex_init(&m_lock, nullptr);
    }

    // C++98，下面的两个条件缺一不可(思考为什么？)
    // Singleton(const Singleton&); // 只声明不实现，声明成私有，防止拷贝
    // Singleton& operator=(const Singleton&); // 防止赋值

    // C++11
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;

public:
    static Singleton *GetInstance()
    {
        // 第一个if是用来判断当前是否是第一次调用
        // 因为如果没有这个if判断，每一次都会进行加锁操作，会导致效率下降
        // 加锁只是为了避免第一次创建对象发生重入问题(线程不安全)
        if (m_instance == nullptr)
        {
            pthread_mutex_lock(&m_lock);
            // 第二个if就用来判断是否因该生成单例对象
            if (m_instance == nullptr)
            {
                // 调用的是默认构造函数
                m_instance = new Singleton;
                // 调用的是默认构造函数
                // m_instance1 = new Singleton();
                // 调用的是拷贝构造函数 
                // m_instance2 = new Singleton(m_instance1);
            }
        }
        pthread_mutex_unlock(&m_lock);
        return m_instance;
    }

    // 对于单例模式的析构函数，因为是单例模式，所以全局只会创建一个对象，这个对象占用的空间压根也不会很大。
    // 而且当进程退出后，系统会自动回收进程的资源，所以一般情况下单例模式是不需要写析构函数的。
    // 如果在某些特定的情况下，需要手动析构之类的，如下：
    // 1、可以手动调用析构
    // void Delete()
    // {
    //     delete m_instance;
    // }
    // 2、可以创建一个内部回收类
    class CGarbo
    {
    public:
        ~CGarbo()
        {
            delete m_instance;
        }
    };
    static CGarbo _garbo; // 需要外部初始化

private:
    static Singleton *m_instance;
    static pthread_mutex_t m_lock;
};

// 第二种实现方法(懒汉实现)
// class Singleton
// {
// public:
//     // 指针和引用都可以
//     // 在C++98中会存在线程安全问题，static _inst对象构造初始化并不能保证线程安全
//     // 但是在C++11中线程安全问题被优化了，C++11中static _inst对象构造初始化是线程安全的
//     Singleton& GetInstance()
//     {
//         static Singleton _inst;
//         return _inst;
//     }
// private:
//     Singleton() {}
//     Singleton(const Singleton &) = delete;
//     Singleton &operator=(const Singleton &) = delete;
// };
#endif